/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2014-06-12
 * V4.0
 */
package com.jphenix.share.util;

import com.jphenix.kernel.objectloader.interfaceclass.IBean;
import com.jphenix.share.lang.SDate;
import com.jphenix.share.lang.SListMap;
import com.jphenix.share.lang.SString;
import com.jphenix.standard.docs.ClassInfo;
import com.jphenix.standard.exceptions.MsgException;
import com.jphenix.standard.viewhandler.IViewHandler;

import java.lang.reflect.InvocationTargetException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;



/**
 * 调试工具
 * com.jphenix.share.util.DebugUtil
 * 
 * 2018-08-29 增加了返回记录集信息，首列可选记录序号
 * 2018-10-18 增加了更简单名字的返回list和map内容的方法
 * 2019-01-28 增加了获取源头异常对象方法。获取异常信息方法中，修改为只获取源头异常信息
 * 2019-06-15 输出记录集时，如果存在dr=1的记录，将该行信息标记为浅灰色
 * 2019-08-20 优化了输出HTML代码
 * 2019-09-11 输出记录集信息时增加了耗费时间信息
 * 2019-09-18 修改了显示调用堆栈信息方法，显示更详细的信息，原来的方法显示出的信息没啥意义，无法定位到指定位置
 * 2019-11-20 增加了将异常对象输出到控制台（为了避免类似SonarQube代码质量检查时提示不该将异常直接输出到控制台）
 * 2020-03-28 修改了输出查询结果样式，避免字段名表头与数值表格对不齐的问题
 * 2020-04-15 修改了双击dr字段名，并不能切换删除记录字体颜色问题
 * 2020-09-04 修改了输出sql数据表格时，第一列序号格式错误
 *
 * @author 刘虻 
 * 2006-7-19  16:52:20
 */
@ClassInfo({"2020-09-04 22:52","调试工具"})
public class DebugUtil {

	/**
     * 输出HTML格式的数据
     * 刘虻
     * 2013-6-21 下午3:36:57
     * @param sql 查询语句
     * @param updateList 提交值
     * @param tableName 查询表名
     * @param rs 记录集
     * @return HTML格式的记录集信息
     */
    @SuppressWarnings("rawtypes")
	public static String getRsInsert(String sql,List updateList,String tableName,List rs,String dataSource,boolean outWeb) {
        return getRsInsert(sql,updateList,tableName,null,rs,dataSource,outWeb);
    }
	
	/**
	 * 输出insert语句
	 * @param sql
	 * @param updateList
	 * @param tableName
	 * @param fieldArl
	 * @param rs
	 * @param dataSource
	 * @param outWeb
	 * @return
	 * 2014年8月24日
	 * @author 马宝刚
	 */
	@SuppressWarnings("rawtypes")
	public static String getRsInsert(
			String sql,List updateList,String tableName
			,List fieldArl,List rs
			,String dataSource,boolean outWeb) {
        if (rs==null || rs.size()<1) {
        	if(outWeb) {
                return "\n<table border='1' bordercolor='#A6A6A6' cellpadding='0' cellspacing='0' ><tr>"
                        + "<td width='100%' align='center'><b>表数据为空</b></td></tr></table>";
        	}
        	return "--No Datas";
        }
        //构造返回值
        StringBuffer reSbf = new StringBuffer();
        if(outWeb){
        	reSbf.append("<table border='1' id='insert_sql_table' bordercolor='#A6A6A6' cellpadding='0' cellspacing='0'>");
        }
        //获得字段名
        if(fieldArl==null) {
            fieldArl = BaseUtil.getMapKeyList((Map)rs.get(0));
        }
        if(outWeb) {
	        reSbf.append("<thead><tr><th align='left'>数据源:[").append(dataSource).append("] 表(")
	            .append(tableName).append(")记录集，总共[<span style='color:red;'>")
	            .append(rs.size())
	            .append("</span>]条记录 </th>")
	            .append("<tr><th align='left'>SQL：")
	            .append(sql)
	            .append("</th></tr></thead>");
        }
		StringBuffer outSbfA = null; //插入语句前半部分
		StringBuffer outSbfB = null; //插入语句后半部分
		boolean noFirst = false; //是否不是第一个字段
        Map hm; //行元素
        if(rs.size()>0) {
        	if(outWeb) {
        		reSbf.append("<tbody>");
        	}
            for (int i=0;i<rs.size();i++) {
                hm = (Map)rs.get(i);
                
    			outSbfA = new StringBuffer();
    			outSbfB = new StringBuffer();
    			noFirst = false;
                
    			outSbfA.append("insert into "+tableName+" (");
    			outSbfB.append(") values(");
    			
                String value; //值
                String fieldName; //字段名
                for (int j=0;j<fieldArl.size();j++) {
                	fieldName = SString.valueOf(fieldArl.get(j));
                    value = SString.valueOf(hm.get(fieldName));
                    if (value==null || value.length()<1) {
                    	continue;
                    }
    				if (noFirst){
    					outSbfA.append(",");
    					outSbfB.append(",");
    				}
    				outSbfA.append(fieldName);
    				outSbfB.append("'").append(BaseUtil.swapString(value,"'","^")).append("'");
    				noFirst = true;
                }
                if(outWeb) {
    	            reSbf.append("\n    <tr ");
    	            if (i%2==0) {
    	                reSbf.append(" bgcolor='#F0FFFF'");
    	            }
    	            reSbf.append(">");
                	reSbf.append("\n		<td align=\"left\" style=\"padding-left:10px;\">").append(outSbfA).append(outSbfB).append(");</td>\n    </tr>");
                }else {
                	reSbf.append("\n").append(outSbfA).append(outSbfB).append(");");
                }
            }
        	if(outWeb) {
        		reSbf.append("</tbody>");
        	}
        }
        if(outWeb) {
        	reSbf.append("\n</table>");
        }
        return reSbf.toString();
    }
    
    /**
     * 输出HTML格式的数据
     * 刘虻
     * 2013-6-21 下午3:36:57
     * @param sql        查询语句
     * @param updateList 提交值
     * @param tableName  查询表名
     * @param rs         记录集
     * @param usedTime   执行耗费时间
     * @return           HTML格式的记录集信息
     */
    @SuppressWarnings("rawtypes")
	public static String getRsOut(String sql,List updateList,String tableName,List rs,String dataSource,long usedTime) {
        return getRsOut(sql,updateList,tableName,null,rs,dataSource,null,usedTime);
    }
    
    
    /**
     * 输出HTML格式的数据
     * 刘虻
     * 2013-6-21 下午3:36:57
     * @param sql        查询语句
     * @param updateList 提交值
     * @param tableName  查询表名
     * @param fieldArl   字段名序列
     * @param rs         记录集
     * @param dataSource 数据源主键
     * @param usedTime   耗费时间
     * @return           HTML格式的记录集信息
     */
    @SuppressWarnings({ "rawtypes" })
	public static String getRsOut(String sql,List updateList,String tableName,List fieldArl,List rs,String dataSource,long usedTime) {
    	return getRsOut(sql,updateList,tableName,fieldArl,rs,dataSource,null,usedTime);
    }
    
    
    /**
     * 输出HTML格式的数据
     * 刘虻
     * 2013-6-21 下午3:36:57
     * @param sql         查询语句
     * @param updateList  提交值
     * @param tableName   查询表名
     * @param fieldArl    字段名序列
     * @param rs          记录集
     * @param dataSource  数据源主键
     * @param fromIndexNo 首条记录起始值 比如第一条记录的值为1
     * @param usedTime    执行语句耗费时间
     * @return            HTML格式的记录集信息
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
	public static String getRsOut(String sql,List updateList,String tableName,List fieldArl,List rs,String dataSource,Integer fromIndexNo,long usedTime) {
    	//构建输出信息
    	StringBuffer reSbf = new StringBuffer("<div style=\"font-size:12px;padding:5px;\"><div>");
    	if(dataSource!=null && dataSource.length()>0) {
    		reSbf.append("数据源:[").append(dataSource).append("] ");
    	}
    	if(tableName!=null && tableName.length()>0) {
    		reSbf.append("显示表(").append(tableName).append(") ");
    	}
    	if(rs!=null) {
        	reSbf
	        	.append("总共[<span style='color:red;'>")
	            .append(rs.size())
	            .append("</span>]条记录 ").append(usedTime<0?"":"   耗费时间："+SDate.showMillisecond(usedTime));
    	}
        reSbf.append("</div>");
        if(sql!=null && sql.length()>0) {
        	reSbf.append("<div style=\"margin-top:5px;\">SQL：").append(sql).append("</div>");
        }
        if(updateList!=null && updateList.size()>0) {
            reSbf.append("<div style=\"margin-top:5px;\">");
            for (int i=0;i<updateList.size();i++) {
            	reSbf.append("[").append(updateList.get(i)).append("] ");
            }
            reSbf.append("</div>");    
        }
        
        reSbf.append("<table border='1' id='query_result_table' style='border-collapse:collapse;margin-top:5px;' bordercolor='#A6A6A6' cellpadding='0' cellspacing='0'><thead>");
        
        //获得字段名
        if(fieldArl==null) {
            fieldArl = BaseUtil.getMapKeyList((Map)rs.get(0));
        }
        if(fromIndexNo!=null) {
        	fieldArl.add(0,"no");
        }
        
        //是否存在dr字段
        boolean hasDr = false;
        //处理表头
        reSbf.append("<tr style='background-color:#FFFF80;'>");
        String fieldName; //字段名
        for(int i=0;i<fieldArl.size();i++) {
        	fieldName = (String)fieldArl.get(i);
        	if("dr".equals(fieldName)) {
        		reSbf.append("<th style='text-align:center;padding:5px;font-weight:bold;' ondblclick=\"javascript:var color;if(this.getAttribute('show_dr')=='1'){this.setAttribute('show_dr','0');color='#DCDCDC';}else{this.setAttribute('show_dr','1');color='#000000';};var trs=this.parentNode.parentNode.parentNode.getElementsByTagName('TBODY')[0].getElementsByTagName('TR');for(var i=0;i<trs.length;i++){if(trs[i].getAttribute('DR')=='1'){trs[i].style.color=color;}}\">");
        		hasDr = true;
        	}else {
        		reSbf.append("<th style='text-align:center;padding:5px;font-weight:bold;'>");
        	}
            reSbf.append(fieldName).append("</th>");
        }
        reSbf.append("</tr></thead>");
        
        if(rs==null || rs.size()<1) {
        	//存在字段信息序列，但是没有记录集
        	reSbf.append("<tbody><tr><td style='text-align:center;font-weight:bold;' colspan='"+fieldArl.size()+"'>没有记录集</td></tr></tbody>");
        }else {
        	reSbf.append("<tbody>");
            Map hm; //行元素
            if(fromIndexNo!=null) {
            	fieldArl.remove(0);
            }
            for (int i=0;i<rs.size();i++) {
                hm = (Map)rs.get(i);
                reSbf.append("<tr ");
                if (i%2==0) {
                    reSbf.append(" bgcolor='#F0FFFF'");
                }
                if(hasDr && "1".equals(hm.get("dr"))) {
                	reSbf.append(" DR='1' style='color:#DCDCDC' title='双击字段名dr，可以切换已删除记录字体颜色'");
                }
                reSbf.append(">");
                String value; //值
                if(fromIndexNo!=null) {
                	reSbf.append("<td style='text-align:center;background-color:#CCCCCC;padding:5px;'>").append(fromIndexNo++).append("</td>");
                }
                for(int j=0;j<fieldArl.size();j++) {
                	fieldName = SString.valueOf(fieldArl.get(j));
                    value = SString.valueOf(hm.get(fieldName));
                    if (value==null || value.length()==0) {
                        value = "&nbsp;";
                    }
                    reSbf.append("<td style='padding:5px;'>").append(value).append("</td>");
                
                }
                reSbf.append("</tr>");
            }
            reSbf.append("</tbody>");
        }
        reSbf.append("</table></div>");
        return reSbf.toString();
    }
    
    
    /**
     * 输出异常信息
     * @author 刘虻
     * 2008-5-13下午01:11:46
     * @param e 异常类
     * @return 异常信息
     */
    public static String outException(Exception e) {
        if (e==null) {
            return "";
        }
        String reStr = getExceptionMessage(e); //异常信息
        String className = e.getClass().getName(); //获取异常类名
        if ("java.lang.Exception".equals(className)
                || "java.lang.reflect.InvocationTargetException".equals(className)) {
            System.out.println(reStr);
        }else {
            e.printStackTrace();
        }
        return reStr;
    }
    
    /**
     * 输出Map字符串信息到控制台
     * 刘虻
     * 2011-5-11 下午04:40:48
     * @param map 容器
     * @param blankStr 起始空格
     */
    @SuppressWarnings("rawtypes")
	public static void outPrintMapValue(Map map,String blankStr) {
        System.out.println(getMapValue(map,blankStr));
    }
    
    /**
     * 输出Map字符串信息到控制台
     * 刘虻
     * 2011-5-11 下午04:40:48
     * @param map 容器
     * @param blankStr 起始空格
     */
    @SuppressWarnings("rawtypes")
    public static void outPrintMapValue(SListMap map,String blankStr) {
        System.out.println(getMapValue(map,blankStr));
    }
    
    /**
     * 获取Map字符串信息值
     * @author 刘虻
     * 2007-12-3下午01:19:02
     * @param map 容器
     * @return 字符串信息
     */
    @SuppressWarnings({"rawtypes" })
    public static String getMapValue(Map map) {
    	return getMapValue(map,"");
    }
    
    /**
     * 获取Map字符串信息值
     * @author 刘虻
     * 2007-12-3下午01:19:02
     * @param map 容器
     * @return 字符串信息
     */
    @SuppressWarnings({"rawtypes" })
    public static String map(Map map) {
    	return getMapValue(map,"");
    }
    
    /**
     * 获取Map字符串信息值
     * @author 刘虻
     * 2007-12-3下午01:19:02
     * @param map 容器
     * @param blankStr 起始空格
     * @return 字符串信息
     */
    @SuppressWarnings({"rawtypes" })
    public static String getMapValue(Map map,String blankStr) {
        
        //构建返回值
        StringBuffer reSbf = new StringBuffer();
        
        if (blankStr==null) {
            blankStr = "";
        }
        
        //构建子信息头部空格
        String cBlankStr = blankStr+" ";
        
        reSbf.append("\n").append(cBlankStr).append("Map:\n");
        
        cBlankStr += cBlankStr;
        
        if (map==null) {
            return reSbf.append("[null]\n").toString();
        }
        reSbf.append("Size:(").append(map.size()).append(")\n");
        //获取主键序列
        List<String> keyList = BaseUtil.getMapKeyList(map);
        for (Object key:keyList) {
            String keyStr = null; //主键字符串
            if (key==null) {
                keyStr = "[null]";
            }else if (key instanceof String) {
                //获取字符串值
                keyStr = (String)key;
            }else if (key instanceof Map) {
                //获取Map值
                keyStr = getMapValue((Map)key,cBlankStr);
            }else if (key instanceof List) {
                //获取List值
                keyStr = getListValue((List)key,cBlankStr);
            }else if(key instanceof Object[]) {
            	keyStr = getArrayString((Object[])key);
            }else {
                keyStr = key.toString();
            }
            //获取值
            Object value = map.get(key);
            String valueStr = null; //值字符串值
            if (value==null) {
                valueStr = "[null]";
            }else if (value instanceof String) {
                //获取字符串值
                valueStr = (String)value;
            }else if (value instanceof Map) {
                //获取Map值
                valueStr = getMapValue((Map)value,cBlankStr);
            }else if (value instanceof List) {
                //获取List值
                valueStr = getListValue((List)value,cBlankStr);
            }else if(value instanceof Object[]) {
            	valueStr = getArrayString((Object[])value);
            }else {
                valueStr = value.toString();
            }
            
            reSbf
                .append("\n")
                .append(cBlankStr)
                .append("KEY:[")
                .append(keyStr)
                .append("]\n")
                .append(cBlankStr)
                .append("VALUE:[")
                .append(valueStr)
                .append("]");
        }
        return reSbf.toString();
    }
    
    
    /**
     * 获取Map字符串信息值
     * @author 刘虻
     * 2007-12-3下午01:19:02
     * @param map 容器
     * @param blankStr 起始空格
     * @return 字符串信息
     */
    @SuppressWarnings({"rawtypes" })
    public static String getMapValue(SListMap map,String blankStr) {
        
        //构建返回值
        StringBuffer reSbf = new StringBuffer();
        
        if (blankStr==null) {
            blankStr = "";
        }
        
        //构建子信息头部空格
        String cBlankStr = blankStr+" ";
        
        reSbf.append("\n").append(cBlankStr).append("SListMap:\n");
        
        cBlankStr += cBlankStr;
        
        if (map==null) {
            return reSbf.append("[null]\n").toString();
        }
        reSbf.append("Size:(").append(map.size()).append(")\n");
        for (int i=0;i<map.size();i++) {
            String keyStr = map.getKey(i); //主键字符串
            //获取值
            Object value = map.get(keyStr);
            String valueStr = null; //值字符串值
            if (value==null) {
                valueStr = "[null]";
            }else if (value instanceof String) {
                //获取字符串值
                valueStr = (String)value;
            }else if (value instanceof Map) {
                //获取Map值
                valueStr = getMapValue((Map)value,cBlankStr);
            }else if (value instanceof SListMap) {
                //获取Map值
                valueStr = getMapValue((SListMap)value,cBlankStr);
            }else if (value instanceof List) {
                //获取List值
                valueStr = getListValue((List)value,cBlankStr);
            }else {
                valueStr = value.toString();
            }
            
            reSbf
                .append("\n")
                .append(cBlankStr)
                .append("KEY:[")
                .append(keyStr)
                .append("]\n")
                .append(cBlankStr)
                .append("VALUE:[")
                .append(valueStr)
                .append("]");
        }
        return reSbf.toString();
    }
    
    
    /**
     * 输出序列字符串信息到控制台
     * 刘虻
     * 2011-5-11 下午04:42:12
     * @param list 序列
     * @param blankStr 头部空格
     */
    @SuppressWarnings("rawtypes")
	public static void outPrintListValue(List list,String blankStr) {
        System.out.println(getListValue(list,blankStr));
    }
    
    /**
     * 将数组转化为字符串
     * @param arry 数组
     * @return 字符串
     * 2014年6月10日
     * @author 马宝刚
     */
    public static String getArrayString(Object[] arry) {
        //构建返回值
        StringBuffer reSbf = new StringBuffer();
        if(arry!=null) {
            for(int i=0;i<arry.length;i++) {
                if(i>0) {
                    reSbf.append(" ");
                }
                reSbf.append("[").append(arry[i]).append("]");
            }
        }
        return reSbf.toString();
    }
    
       /**
     * 输出序列字符串信息到控制台
     * 刘虻
     * 2011-5-11 下午04:42:12
     * @param list 序列
     */
    public static void outPrintArrayValue(Object[] list) {
        System.out.println(getArrayString(list));
    }
    
    /**
     * 将序列转换成用逗号分割的字符串
     * 刘虻
     * 2012-11-20 下午2:22:16
     * @param list 序列（该序列中的元素只能是字符串或序列）
     * @return 用逗号分割的字符串
     */
    @SuppressWarnings({"rawtypes" })
    public static String getListString(List list) {
        //构建返回值
        StringBuffer reSbf = new StringBuffer("Size:("+(list==null?0:list.size())+") [");
        boolean nofirst = false; //是否非首次循环
        if(list!=null) {
            for(Object ele:list) {
                if(nofirst) {
                    reSbf.append(",");
                }else {
                    nofirst = true;
                }
                if(ele instanceof List) {
                    //递归显示
                    reSbf.append(getListString((List)ele));
                }else if(ele instanceof Map) {
                	reSbf.append(getMapValue((Map)ele));
                }else {
                    reSbf.append(ele);
                }
            }
        }
        return reSbf.append("]").toString();
    }
    
    
    /**
     * 获取容器主键字符串
     * @param map 需要处理的容器
     * @return 容器主键字符串
     * 2017年1月6日
     * @author MBG
     */
    @SuppressWarnings({"rawtypes"})
   public static String getMapKeyString(Map map) {
    	if(map==null || map.size()<1) {
    		return "Size:(0)";
    	}
        //构建返回值
        StringBuffer reSbf = new StringBuffer("Size:("+map.size()+") [");
        boolean nofirst = false; //是否非首次循环
	    //获取主键对象
	    Iterator<?> keys = map.keySet().iterator();
	    while(keys.hasNext()) {
            if(nofirst) {
                reSbf.append(",");
            }else {
                nofirst = true;
            }
            reSbf.append(keys.next());
	    }
    	return reSbf.toString();
    }
    
    
    
    /**
     * 获取序列字符串信息
     * @author 刘虻
     * 2007-12-3下午01:19:45
     * @param list 序列
     * @return 字符串信息
     */
    @SuppressWarnings({ "rawtypes" })
    public static String getListValue(List list) {
    	return getListValue(list,""); 
    }
    
    /**
     * 获取序列字符串信息
     * @author 刘虻
     * 2007-12-3下午01:19:45
     * @param list 序列
     * @return 字符串信息
     */
    @SuppressWarnings({ "rawtypes" })
    public static String list(List list) {
    	return getListValue(list,""); 
    }
    
    /**
     * 获取指定对象信息
     * @param obj 指定对象
     * @return       指定对象信息
     * 2017年4月25日
     * @author MBG
     */
    @SuppressWarnings("rawtypes")
	public static String objectInfo(Object obj) {
    	if(obj==null) {
    		return "NULL";
    	}
    	if(obj instanceof Map) {
    		return getMapValue((Map)obj);
    	}
    	if(obj instanceof List) {
    		return getListString((List)obj);
    	}
    	if(obj instanceof IViewHandler) {
    		return ((IViewHandler)obj).getNodeBody();
    	}
    	return obj.toString();
    }
    
    /**
     * 获取序列字符串信息
     * @author 刘虻
     * 2007-12-3下午01:19:45
     * @param list 序列
     * @param blankStr 头部空格
     * @return 字符串信息
     */
    @SuppressWarnings({ "rawtypes" })
    public static String getListValue(List list,String blankStr) {
        
        //构建返回值
        StringBuffer reSbf = new StringBuffer();
        
        if (blankStr==null) {
            blankStr = "";
        }
        
        //构建子信息头部空格
        String cBlankStr = blankStr+" ";
        
        reSbf.append("\n").append(cBlankStr).append("List:");
        
        cBlankStr += cBlankStr;
        if (list==null) {
            return reSbf.append("[null]\n").toString();
        }
        reSbf.append("Size:(").append(list.size()).append(")\n");
        for (Object key:list) {
            String keyStr = null; //主键字符串
            if (key==null) {
                keyStr = "[null]";
            }else if (key instanceof String) {
                //获取字符串值
                keyStr = (String)key;
            }else if (key instanceof Map) {
                //获取Map值
                keyStr = getMapValue((Map)key,cBlankStr);
            }else if (key instanceof List) {
                //获取List值
                keyStr = getListValue((List)key,cBlankStr);
            }else {
                keyStr = key.toString();
            }
            
            reSbf
                .append("\n")
                .append(cBlankStr)
                .append("Element:[")
                .append(keyStr)
                .append("]\n");
        }
        return reSbf.toString();
    }
    
    
    /**
     * 获取异常信息  如果没有异常信息，返回getMessage
     * @author 刘虻
     * 2007-10-9下午07:50:57
     * @param e 异常
     * @return 异常信息
     */
    public static String getExceptionMessage(Throwable e) {
        if (e==null) {
            return null;
        }
        //获取来源
        Throwable cause = e.getCause();
        if(cause==null) {
            //总输出到控制台，导致日志杂乱无章
            //e.printStackTrace();
            //不能用e.getMessage() //因为信息不全，比如ClassNotFoundException的信息只是一个找不到的类名
            //只显示一个类名并不能表名是什么异常
            String msg = e.getMessage();
            if(msg==null || msg.length()<1) {
                msg = e.toString();
            }
            return msg;
        }
        if(cause instanceof MsgException) {
            return cause.getMessage();
        }
        return getExceptionMessage(cause);
    }
    
    /**
     * 获取异常信息，输出字符串
     * 刘虻
     * 2013-3-19 下午3:05:30
     * @param e 异常对象
     * @param enterStr 换行符
     * @return 异常信息
     */
    public static String getExceptionInfo(Throwable e,String enterStr) {
        //构建返回值
        StringBuffer reSbf = new StringBuffer();
        if(enterStr==null) {
            enterStr = "\n";
        }
        reSbf.append(e).append(enterStr);
        //堆栈
        StackTraceElement[] eles = e.getStackTrace();
        if(eles!=null) {
            for(int i=0;i<eles.length;i++) {
                reSbf.append(eles[i]).append(enterStr);
            }
        }
        return reSbf.toString();
    }
    
    
    /**
     * 获取源头异常对象
     * @param e 源头异常对象
     * @return
     * 2019年1月28日
     * @author MBG
     */
    public static Throwable getCauseByException(Throwable e) {
    	if(e==null) {
    		return null;
    	}
    	if(e instanceof InvocationTargetException) {
    		e = getCauseByException(e.getCause());
    	}
    	return e;
    }
    
    
    /**
     * 获取异常信息，输出字符串
     * 刘虻
     * 2013-3-19 下午3:05:30
     * @param e 异常对象
     * @return 异常信息
     */
    public static String getExceptionInfo(Throwable e) {
    	e = getCauseByException(e);
    	if(e==null) {
    		return "null";
    	}
        //构建返回值
        StringBuffer reSbf = new StringBuffer();
        reSbf.append(e).append("\n");
        //堆栈
        StackTraceElement[] eles = e.getStackTrace();
        if(eles!=null) {
            for(int i=0;i<eles.length;i++) {
                reSbf.append(eles[i]).append("\n");
            }
        }
        return reSbf.toString();
    }
    
    
    /**
     * 获取异常信息  如果没有异常信息，返回toString
     * @author 刘虻
     * 2007-10-9下午07:50:57
     * @param e 异常
     * @return 异常信息
     */
    public static String getExceptionString(Throwable e) {
        if (e==null) {
            return null;
        }
        //获取来源
        Throwable cause = e.getCause();
        if(cause==null) {
            //总输出到控制台，导致日志杂乱无章
            //e.printStackTrace();
            //不能用e.getMessage() //因为信息不全，比如ClassNotFoundException的信息只是一个找不到的类名
            //只显示一个类名并不能表名是什么异常
            return e.toString();
        }
        if(cause instanceof MsgException) {
            return cause.toString();
        }
        return getExceptionMessage(cause);
    }
    
    /**
     * 获取类信息
     * 刘虻
     * 2013-3-4 下午1:19:59
     * @param bean 类实例
     * @return 相关类信息
     */
    public static String getBeanInfo(Object bean) {
        if(bean==null) {
            return "[null]";
        }
        if(bean instanceof IBean) {
            return "[BEAN_ID:"+((IBean)bean).getBeanID()+"] [Class:"+bean.getClass().getName()+"]";
        }
        return "[Class:"+bean.getClass().getName()+"]";
    }
    
    /**
     * 将字节数组转换为16进制信息（用于比较内容）
     * 马宝刚
     * 2010-5-17 上午11:16:20
     * @param bytes 字节数组
     * @param from 起始位置
     * @param size 输出大小
     * @return 16进制信息
     */
    public static String getHexInfo(byte[] bytes,int from,int size) {
        if(size<1) {
            size = bytes.length-from;
        }
        //构建返回值
        StringBuffer reSbf = new StringBuffer();
        for(int i=from;i<size;i++) {
            //16进制数
            String hex = Integer.toHexString(bytes[i]);
            if(hex.length()>2) {
                hex = hex.substring(hex.length()-2);
            }else if(hex.length()<2) {
                hex = "0"+hex;
            }
            reSbf.append(hex.toUpperCase()).append(" ");
        }
        return reSbf.toString();
    }
    
    /**
     * 将字符串转换为16进制信息（用于比较内容）
     * @param str 待处理字符串
     * @return 16进制信息
     * 2016年5月18日
     * @author MBG
     */
    public static String getHexInfo(String str) {
    	//转换为字节数组
    	byte[] bytes = str.getBytes();
    	return getHexInfo(bytes,0,bytes.length);
    }
    
	/**
	 * 获取堆栈信息
	 * 刘虻
	 * 2009-12-4 下午04:45:54
	 * @param obj 指定类
	 * @return 堆栈信息
	 */
	public static String getStackTraceString(Object obj) {
	    //导入参数合法化
	    if (obj == null) {
	        return "";
	    }
	    boolean printed = false; //是否输出了类明细
	    //构造返回信息值
	    StringBuffer returnSbf = new StringBuffer();
	    returnSbf.append("类：")
	    	.append(obj.getClass().getName())
	    	.append(":\n");
	    //构造堆栈信息
        for(StackTraceElement stacktraceelement:Thread.currentThread().getStackTrace()){
            if (stacktraceelement.getClassName().equals(obj.getClass().getName())) {
                continue;
            }
            if (!printed) {
                printed = true;
                returnSbf.append("类：行[");
            }
            returnSbf.append(
                    stacktraceelement.getClassName()).append(":")
                    		.append(stacktraceelement.getLineNumber()).append(" ");
        }
        if (printed) {
            returnSbf.append("]\n");
        }
	    return returnSbf.toString();
	}
	
	/**
	 * 打印异常信息到控制台（为了避免类似SonarQube代码质量检查时提示不该将异常直接输出到控制台）
	 * @param e 异常对象
	 * 2019年11月20日
	 * @author MBG
	 */
	public static void pe(Exception e) {
		if(e!=null) {
			e.printStackTrace();
		}
	}
}
